package sketch.generator;

import randoop.RMethod;
import randoop.StatementKind;
import sketch.ExecutableSequenceContent;
import sketch.MethodExecOutcome;
import sketch.SMethod;
import sketch.SketchSequence;
import sketch.util.Checker;
import sketch.util.RandoopUtil;
import sketch.util.SketchUtil;

public class SingleSequenceGenerator<T extends SketchSequence> extends SequenceGenerator<T> {
	
	protected SketchSequence generatedSequence;
	
	public SingleSequenceGenerator(Object receiver, SMethod smethod, ValueGenerator<? extends Object> generator) {
		super(receiver, smethod, generator);
		Checker.checkTrue(smethod.getMethodNum() == 1, "The number of method in SMethod object should be 1.");
	}
	
	@Override
	protected SketchSequence generateNext() {
		System.out.println("-- call generateNext() in SingleSequenceGenerator.");
		if(!hasNext()) {
			throw new IterationException("The state has not been reset, " +
					"in single sequence generation.");
		}
		generatedSequence = composeSequence();
		return generatedSequence;
	}

	@Override
	public boolean hasNext() {
		return isReset();
	}
	
	@Override
	public void reset() {
		super.reset();
		this.generatedSequence = null;
	}
	
	@Override
	public ExecutableSequenceContent[] executables() {
		ExecutableSequenceContent[] contents = new ExecutableSequenceContent[1]; 
		                          
		String lookupMethodName = super.smethod.getMethodNames()[0];
		if(lookupMethodName.indexOf("<init>") != -1) {
			lookupMethodName = "<init>()";
		}
		StatementKind s = GenTests.lookupForStatement(this.smethod.sclazz.clazz, lookupMethodName);
		Checker.checkNull(s, "StatementKind for method: " +lookupMethodName + " in class: " + this.smethod.sclazz.clazz + " is null!");

		Object arg = this.valueGenerator.current();
		boolean hasArgument = RandoopUtil.hasParam(s);
		
		ExecutableSequenceContent content = hasArgument ?
				new ExecutableSequenceContent(super.getReceiver(), s, arg) :
					new ExecutableSequenceContent(super.getReceiver(), s);
		contents[0] = content;
		
		return contents;
	}
	
//	@Override
//	public Object[] evaluate() {
//		Object[] results = new Object[1];
//		
//		String lookupMethodName = super.smethod.getMethodNames()[0];
//		boolean isInit = false;
//		if(lookupMethodName.indexOf("<init>") != -1) {
//			lookupMethodName = "<init>()";
//			isInit = true;
//		}
//		StatementKind s = GenTests.lookupForStatement(this.smethod.sclazz.clazz, lookupMethodName);
//		Checker.checkNull(s, "StatementKind for method: " +lookupMethodName + " in class: " + this.smethod.sclazz.clazz + " is null!");
//
//		Object arg = this.valueGenerator.current();
//		boolean hasArgument = RandoopUtil.hasParam(s);
//		
//		ExecutableSequenceContent content = hasArgument ?
//				new ExecutableSequenceContent(super.getReceiver(), s, arg) :
//					new ExecutableSequenceContent(super.getReceiver(), s);
//		results[0] = content.excute().getReturnObject();
//		
//		return results;
//	}

	@Override
	protected SketchSequence composeSequence() {
		
		String lookupMethodName = super.smethod.getMethodNames()[0];
		boolean isInit = false;
		if(lookupMethodName.indexOf("<init>") != -1) {
			lookupMethodName = "<init>()";
			isInit = true;
		}
		StatementKind s = GenTests.lookupForStatement(this.smethod.sclazz.clazz, lookupMethodName);
		Checker.checkNull(s, "StatementKind for method: " +lookupMethodName + " in class: " + this.smethod.sclazz.clazz + " is null!");
		
		Object arg = "";
		if(RandoopUtil.getParamNum(s) != 0) {
		  if(!super.valueGenerator.hasNext()) {
			throw new IterationException("There is no more value for ValueGenerator.");
		  }
		  arg = super.valueGenerator.next();
		}
		SketchSequence sequence = new SketchSequence();
		
		
		String method = SketchUtil.removeMethodParam(super.smethod.getMethodNames()[0]);
//		sequence.add("calling: " + super.reciever.getClass() + ": " + super.smethod.getMethodNames()[0]
//		      + " with value: " + arg);
		
		boolean hasArgument = true;
		if(s instanceof RMethod) {
			RMethod rmethod = (RMethod)s;
			if(rmethod.getMethod().getParameterTypes().length == 0) {
				hasArgument = false;
			}
		}
		
		if(!hasArgument) {
			arg = "";
		}
		
		Class<?> clazz = this.smethod.sclazz.clazz;
		if(!isInit) {
		  sequence.addText(SketchSequence.RECEIVER//SketchUtil.createVarName(super.getReceiver().getClass())
				+ "." + method + "(" +  arg + ");");
		  //reflective execution
//		  if (!hasArgument) {
//			  arg = new Object[0];
//		  }
		  
		  //ReflectionExecutionOutcome execResult = null;
		  ExecutableSequenceContent content = null;
		  if(!hasArgument) {
//		      execResult = ReflectiveExecutions.reflectiveExecute(super.getReceiver(), s);
		      content = new ExecutableSequenceContent(super.getReceiver(), s);
		  } else {
//			  execResult = ReflectiveExecutions.reflectiveExecute(super.getReceiver(), s, arg);
			  content = new ExecutableSequenceContent(super.getReceiver(), s, arg);
		  }
//		  System.out.println("in single sequence execution: " +  execResult
//				  + ",  the object id of receiver: " + this.getReceiverId());
//		  sequence.addExecutableContent(content);
		} else {
		  sequence.addText(clazz.getName() + " " +  SketchSequence.RECEIVER//SketchUtil.createVarName(clazz)
				+ " = new " + clazz.getName() + "(" +  arg + ");");
		}
		
		return sequence;
	}

	@Override
	public SketchSequence getGeneratedSequence() {
		Checker.checkNull(this.generatedSequence, "There is no sequence generated in SingleSequenceGenerator!");
		return generatedSequence;
	}

//	@Override
//	protected MethodExecOutcome[] executionResult() {
//		String lookupMethodName = super.smethod.getMethodNames()[0];
//		if(lookupMethodName.equals("<init>")) {
//			lookupMethodName = "<init>()";
//		}
//		StatementKind s = GenTests.lookupForStatement(this.smethod.sclazz.clazz, lookupMethodName);
//		Checker.checkNull(s, "StatementKind for method: " +lookupMethodName + " is null!");
//		
//		Class<?> retType = RandoopUtil.getReturnType(s);
//		if(retType.equals(void.class)) {
//			return null;
//		}
//		
//		return new MethodExecOutcome[]{new MethodExecOutcome()};
//	}
}
